﻿// Copyright (c) JosephZheng. All rights reserved. See License.txt in the project root for license

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using Lyra.Cache.Client.Attribute;
using Lyra.Cache.Client.Enum;
using Lyra.Cache.Server.Interface;
using Lyra.Cache.Server.Interface.Contract;
using Newtonsoft.Json;
using Lyra.Cache.Server.Interface.Contract.ClientResult;
using Lyra.Cache.Server.Interface.Contract.ServerResult;

namespace Lyra.Cache.Client
{
    public class CacheClient : IDisposable
    {
        public string[] Servers { get; private set; }
        public RAIM Mode { get; private set; }

        private Dictionary<string, string[]> _regionMaps = null;

        public CacheClient(string[] servers, RAIM mode)
        {
            ServicePointManager.DefaultConnectionLimit = 1024;
            ServicePointManager.MaxServicePointIdleTime = 60000;
            Servers = servers;
            Mode = mode;
            _regionMaps = new Dictionary<string, string[]>();
        }

        public static string GetSchema(Type type)
        {
            if (type.IsClass && type.GetCustomAttributes(typeof(CacheContract), true).Length != 0)
            {
                StringBuilder sb = new StringBuilder();
                foreach (PropertyInfo pi in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.SetProperty))
                {
                    if (pi.GetCustomAttributes(typeof(CacheMember), true).Length != 0)
                    {
                        if (pi.PropertyType == typeof(bool) || pi.PropertyType == typeof(bool?))
                        {
                            sb.Append(pi.Name);
                            sb.Append(";System.Boolean#");
                        }
                        else if (pi.PropertyType == typeof(byte) || pi.PropertyType == typeof(byte?))
                        {
                            sb.Append(pi.Name);
                            sb.Append(";System.Byte#");
                        }
                        else if (pi.PropertyType == typeof(char) || pi.PropertyType == typeof(char?))
                        {
                            sb.Append(pi.Name);
                            sb.Append(";System.Char#");
                        }
                        else if (pi.PropertyType == typeof(DateTime) || pi.PropertyType == typeof(DateTime?))
                        {
                            sb.Append(pi.Name);
                            sb.Append(";System.DateTime#");
                        }
                        else if (pi.PropertyType == typeof(decimal) || pi.PropertyType == typeof(decimal?))
                        {
                            sb.Append(pi.Name);
                            sb.Append(";System.Decimal#");
                        }
                        else if (pi.PropertyType == typeof(double) || pi.PropertyType == typeof(double?))
                        {
                            sb.Append(pi.Name);
                            sb.Append(";System.Double#");
                        }
                        else if (pi.PropertyType == typeof(short) || pi.PropertyType == typeof(short?))
                        {
                            sb.Append(pi.Name);
                            sb.Append(";System.Int16#");
                        }
                        else if (pi.PropertyType == typeof(int) || pi.PropertyType == typeof(int?))
                        {
                            sb.Append(pi.Name);
                            sb.Append(";System.Int32#");
                        }
                        else if (pi.PropertyType == typeof(long) || pi.PropertyType == typeof(long?))
                        {
                            sb.Append(pi.Name);
                            sb.Append(";System.Int64#");
                        }
                        else if (pi.PropertyType == typeof(sbyte) || pi.PropertyType == typeof(sbyte?))
                        {
                            sb.Append(pi.Name);
                            sb.Append(";System.SByte#");
                        }
                        else if (pi.PropertyType == typeof(float) || pi.PropertyType == typeof(float?))
                        {
                            sb.Append(pi.Name);
                            sb.Append(";System.Single#");
                        }
                        else if (pi.PropertyType == typeof(string))
                        {
                            sb.Append(pi.Name);
                            sb.Append(";System.String#");
                        }
                        else if (pi.PropertyType == typeof(TimeSpan) || pi.PropertyType == typeof(TimeSpan?))
                        {
                            sb.Append(pi.Name);
                            sb.Append(";System.TimeSpan#");
                        }
                        else if (pi.PropertyType == typeof(ushort) || pi.PropertyType == typeof(ushort?))
                        {
                            sb.Append(pi.Name);
                            sb.Append(";System.UInt16#");
                        }
                        else if (pi.PropertyType == typeof(uint) || pi.PropertyType == typeof(uint?))
                        {
                            sb.Append(pi.Name);
                            sb.Append(";System.UInt32#");
                        }
                        else if (pi.PropertyType == typeof(ulong) || pi.PropertyType == typeof(ulong?))
                        {
                            sb.Append(pi.Name);
                            sb.Append(";System.UInt64#");
                        }
                    }
                }
                if (sb.Length == 0) return null;
                else return sb.Remove(sb.Length - 1, 1).ToString();
            }
            else return null;
        }

        #region internal method
        internal string[] MapRegion(string region)
        {
            if (Mode == RAIM.Mode1 || Servers.Length == 1)
            {
                return Servers;
            }
            else
            {
                lock (_regionMaps)
                {
                    if (_regionMaps.ContainsKey(region))
                    {
                        return _regionMaps[region];
                    }
                    else
                    {
                        long sum = 0;
                        foreach (byte b in Encoding.Unicode.GetBytes(region))
                        {
                            sum += (int)b;
                        }
                        if (Mode == RAIM.Mode0)
                        {
                            long index = sum % Servers.LongLength;
                            string[] result = new string[] { Servers[index] };
                            _regionMaps.Add(region, result);
                            return result;
                        }
                        else if (Mode == RAIM.Mode5)
                        {
                            long index = sum % Servers.LongLength;
                            long index2 = (index + 1 == Servers.LongLength) ? 0 : index + 1;
                            string[] result = new string[] { Servers[index], Servers[index2] };
                            _regionMaps.Add(region, result);
                            return result;
                        }
                        else if (Mode == RAIM.Mode5Plus)
                        {
                            if (Servers.Length <= 3)
                            {
                                _regionMaps.Add(region, Servers);
                                return Servers;
                            }
                            else
                            {
                                long index = sum % Servers.LongLength;
                                long index2 = (index + 1 == Servers.LongLength) ? 0 : index + 1;
                                long index3 = (index2 + 1 == Servers.LongLength) ? 0 : index2 + 1;
                                string[] result = new string[] { Servers[index], Servers[index2], Servers[index3] };
                                _regionMaps.Add(region, result);
                                return result;
                            }
                        }
                    }
                    return new string[0];
                }
            }
        }

        internal object CloneCacheObject(object obj)
        {
            Type type = obj.GetType();
            if (type.IsClass && type.GetCustomAttributes(typeof(CacheContract), true).Length != 0)
            {
                object result = Activator.CreateInstance(type);
                foreach (PropertyInfo pi in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.SetProperty))
                {
                    if (pi.GetCustomAttributes(typeof(CacheMember), true).Length != 0)
                    {
                        pi.SetValue(result, pi.GetValue(obj, null), null);
                    }
                }
                return result;
            }
            else return null;
        }

        internal void ProcessGet<T>(string server, GetClientResult<T, GetServerResult> clientResult, GetServerResult serverResult)
        {
            if (serverResult.Code == CacheCode.OK)
            {
                if (clientResult.Object == null || (serverResult.Json.Time.HasValue && serverResult.Json.Time.Value > clientResult.Object.Time))
                {
                    if (typeof(T).Equals(typeof(string)))
                    {
                        clientResult.Object = new CacheObject<T>()
                        {
                            Data = (T)Convert.ChangeType(serverResult.Json.Data, typeof(T)),
                            Time = serverResult.Json.Time
                        };
                    }
                    else
                    {
                        clientResult.Object = new CacheObject<T>()
                        {
                            Data = JsonConvert.DeserializeObject<T>(serverResult.Json.Data),
                            Time = serverResult.Json.Time
                        };
                    }
                }
                clientResult.ServerResults.Add(server, serverResult);
            }
            else if (serverResult.Code == CacheCode.KeyNotExist || serverResult.Code == CacheCode.RegionNotExist)
            {
                clientResult.ServerResults.Add(server, serverResult);
            }
            else
            {
                clientResult.HasError = false;
                clientResult.ServerResults.Add(server, serverResult);
            }
        }

        internal void GetProcess<T>(string server, GetWithFilterClientResult<T, GetWithFilterServerResult> clientResult, GetWithFilterServerResult serverResult)
        {
            if (serverResult.Code == CacheCode.OK)
            {
                foreach (KeyValuePair<string, GetServerResult> kvp in serverResult.Jsons)
                {
                    if (clientResult.Objects.ContainsKey(kvp.Key))
                    {
                        if (kvp.Value.Json.Time.HasValue && kvp.Value.Json.Time.Value > clientResult.Objects[kvp.Key].Time)
                        {
                            if (typeof(T).Equals(typeof(string)))
                            {
                                clientResult.Objects[kvp.Key] = new CacheObject<T>()
                                {
                                    Data = (T)Convert.ChangeType(kvp.Value.Json.Data, typeof(T)),
                                    Time = kvp.Value.Json.Time
                                };
                            }
                            else
                            {
                                clientResult.Objects[kvp.Key] = new CacheObject<T>()
                                {
                                    Data = JsonConvert.DeserializeObject<T>(kvp.Value.Json.Data),
                                    Time = kvp.Value.Json.Time
                                };
                            }
                        }
                    }
                    else
                    {
                        if (typeof(T).Equals(typeof(string)))
                        {
                            clientResult.Objects[kvp.Key] = new CacheObject<T>()
                            {
                                Data = (T)Convert.ChangeType(kvp.Value.Json.Data, typeof(T)),
                                Time = kvp.Value.Json.Time
                            };
                        }
                        else
                        {
                            clientResult.Objects.Add(kvp.Key, new CacheObject<T>()
                            {
                                Data = JsonConvert.DeserializeObject<T>(kvp.Value.Json.Data),
                                Time = kvp.Value.Json.Time
                            });
                        }
                    }
                }
            }
            else if (serverResult.Code == CacheCode.RegionNotExist)
            {
                clientResult.ServerResults.Add(server, serverResult);
            }
            else
            {
                clientResult.HasError = false;
                clientResult.ServerResults.Add(server, serverResult);
            }
        }

        internal void GetProcess<T>(string server, GetWithExtensionClientResult<T, GetWithExtensionServerResult> clientResult, GetWithExtensionServerResult serverResult)
        {
            if (serverResult.Code == CacheCode.OK)
            {
                foreach (KeyValuePair<string, GetServerResult> kvp in serverResult.Jsons)
                {
                    if (clientResult.Objects.ContainsKey(kvp.Key))
                    {
                        if (kvp.Value.Json.Time.HasValue && kvp.Value.Json.Time.Value > clientResult.Objects[kvp.Key].Time)
                        {
                            if (typeof(T).Equals(typeof(string)))
                            {
                                clientResult.Objects[kvp.Key] = new CacheObject<T>()
                                {
                                    Data = (T)Convert.ChangeType(kvp.Value.Json.Data, typeof(T)),
                                    Time = kvp.Value.Json.Time
                                };
                            }
                            else
                            {
                                clientResult.Objects[kvp.Key] = new CacheObject<T>()
                                {
                                    Data = JsonConvert.DeserializeObject<T>(kvp.Value.Json.Data),
                                    Time = kvp.Value.Json.Time
                                };
                            }
                        }
                    }
                    else
                    {
                        if (typeof(T).Equals(typeof(string)))
                        {
                            clientResult.Objects[kvp.Key] = new CacheObject<T>()
                            {
                                Data = (T)Convert.ChangeType(kvp.Value.Json.Data, typeof(T)),
                                Time = kvp.Value.Json.Time
                            };
                        }
                        else
                        {
                            clientResult.Objects.Add(kvp.Key, new CacheObject<T>()
                            {
                                Data = JsonConvert.DeserializeObject<T>(kvp.Value.Json.Data),
                                Time = kvp.Value.Json.Time
                            });
                        }
                    }
                }
            }
            else if (serverResult.Code == CacheCode.RegionNotExist)
            {
                clientResult.ServerResults.Add(server, serverResult);
            }
            else
            {
                clientResult.HasError = false;
                clientResult.ServerResults.Add(server, serverResult);
            }
        }
        #endregion

        public ClientResult<ServerResult> Create(string region, string schema, Dictionary<string, string> views, Dictionary<string, string> extensions)
        {
            ClientResult<ServerResult> result = new ClientResult<ServerResult>();
            this.MapRegion(region).AsParallel().ForAll(server =>
            {
                try
                {
                    byte[] temp = (byte[])WcfHelper<ICacheServer>.ExecuteMethod(server, "Create", region, schema, views, extensions);
                    ServerResult serverresult = ResultProcessor.DeserializeResult<ServerResult>(temp);
                    lock (result)
                    {
                        if (serverresult.Code != CacheCode.OK)
                        {
                            result.HasError = true;
                        }
                    }
                    result.ServerResults.Add(server, serverresult);
                }
                catch (Exception ex)
                {
                    result.ServerResults.Add(server, new ServerResult() { Code = ex.Message });
                }
            });
            return result;
        }

        public ClientResult<ServerResult> Create(string region, Type type, Dictionary<string, string> views, Dictionary<string, string> extensions)
        {
            string schema = CacheClient.GetSchema(type);
            if (!string.IsNullOrWhiteSpace(schema))
            {
                return this.Create(region, schema, views, extensions);
            }
            else
            {
                return new ClientResult<ServerResult>()
                {
                    HasError = true
                };
            }
        }

        public ClientResult<ServerResult> Create(string region, Type type, Dictionary<string, string> views)
        {
            return this.Create(region, type, views, null);
        }

        public ClientResult<ServerResult> Create(string region, Type type)
        {
            return this.Create(region, type, null, null);
        }

        public ClientResult<ServerResult> Drop(string region)
        {
            ClientResult<ServerResult> result = new ClientResult<ServerResult>();
            this.MapRegion(region).AsParallel().ForAll(server =>
            {
                try
                {
                    byte[] temp = (byte[])WcfHelper<ICacheServer>.ExecuteMethod(server, "Drop", region);
                    ServerResult tempresult = ResultProcessor.DeserializeResult<ServerResult>(temp);
                    lock (result)
                    {
                        if (tempresult.Code != CacheCode.OK)
                        {
                            result.HasError = true;
                        }
                    }
                    result.ServerResults.Add(server, tempresult);
                }
                catch (Exception ex)
                {
                    result.ServerResults.Add(server, new ServerResult() { Code = ex.Message });
                }
            });
            return result;
        }

        public ClientResult<SchemaServerResult> Schema(string region)
        {
            ClientResult<SchemaServerResult> result = new ClientResult<SchemaServerResult>();
            this.MapRegion(region).AsParallel().ForAll(server =>
            {
                try
                {
                    byte[] temp = (byte[])WcfHelper<ICacheServer>.ExecuteMethod(server, "Schema", region);
                    SchemaServerResult serverresult = ResultProcessor.DeserializeResult<SchemaServerResult>(temp);
                    lock (result)
                    {
                        if (serverresult.Code != CacheCode.OK)
                        {
                            result.HasError = true;
                        }
                    }
                    result.ServerResults.Add(server, serverresult);
                }
                catch (Exception ex)
                {
                    result.ServerResults.Add(server, new SchemaServerResult() { Code = ex.Message });
                }
            });
            return result;
        }

        public ClientResult<StatisticsServerResult> Statistics()
        {
            ClientResult<StatisticsServerResult> result = new ClientResult<StatisticsServerResult>();
            this.Servers.AsParallel().ForAll(server =>
            {
                try
                {
                    byte[] temp = (byte[])WcfHelper<ICacheServer>.ExecuteMethod(server, "Statistics");
                    StatisticsServerResult serverresult = ResultProcessor.DeserializeResult<StatisticsServerResult>(temp);
                    lock (result)
                    {
                        if (serverresult.Code != CacheCode.OK)
                        {
                            result.HasError = true;
                        }
                    }
                    result.ServerResults.Add(server, serverresult);
                }
                catch (Exception ex)
                {
                    result.ServerResults.Add(server, new StatisticsServerResult() { Code = ex.Message });
                }
            });
            return result;
        }

        public ClientResult<MemoryServerResult> Memory()
        {
            ClientResult<MemoryServerResult> result = new ClientResult<MemoryServerResult>();
            this.Servers.AsParallel().ForAll(server =>
            {
                try
                {
                    byte[] temp = (byte[])WcfHelper<ICacheServer>.ExecuteMethod(server, "Memory");
                    MemoryServerResult serverresult = ResultProcessor.DeserializeResult<MemoryServerResult>(temp);
                    lock (result)
                    {
                        if (serverresult.Code != CacheCode.OK)
                        {
                            result.HasError = true;
                        }
                    }
                    result.ServerResults.Add(server, serverresult);
                }
                catch (Exception ex)
                {
                    result.ServerResults.Add(server, new MemoryServerResult() { Code = ex.Message });
                }
            });
            return result;
        }

        public ClientResult<ServerResult> Add(string region, string key, object obj, TimeSpan expire)
        {
            ClientResult<ServerResult> result = new ClientResult<ServerResult>();
            object addobj = CloneCacheObject(obj);
            if (addobj != null)
            {
                this.MapRegion(region).AsParallel().ForAll(server =>
                {
                    try
                    {
                        byte[] temp = (byte[])WcfHelper<ICacheServer>.ExecuteMethod(server, "Add", region, key, JsonConvert.SerializeObject(addobj), expire);
                        ServerResult tempresult = ResultProcessor.DeserializeResult<ServerResult>(temp);
                        lock (result)
                        {
                            if (tempresult.Code != CacheCode.OK)
                            {
                                result.HasError = true;
                            }
                        }
                        result.ServerResults.Add(server, tempresult);
                    }
                    catch (Exception ex)
                    {
                        result.ServerResults.Add(server, new ServerResult() { Code = ex.Message });
                    }
                });
            }
            else
            {
                result.HasError = true;
            }
            return result;
        }

        public ClientResult<ServerResult> Set(string region, string key, object obj, TimeSpan expire, bool addIfNotExist)
        {
            ClientResult<ServerResult> result = new ClientResult<ServerResult>();
            object setobj = CloneCacheObject(obj);
            if (setobj != null)
            {
                this.MapRegion(region).AsParallel().ForAll(server =>
                {
                    try
                    {
                        byte[] temp = (byte[])WcfHelper<ICacheServer>.ExecuteMethod(server, "Set", region, key, JsonConvert.SerializeObject(setobj), expire, addIfNotExist);
                        ServerResult tempresult = ResultProcessor.DeserializeResult<ServerResult>(temp);
                        lock (result)
                        {
                            if (tempresult.Code != CacheCode.OK)
                            {
                                result.HasError = true;
                            }
                        }
                        result.ServerResults.Add(server, tempresult);
                    }
                    catch (Exception ex)
                    {
                        result.ServerResults.Add(server, new ServerResult() { Code = ex.Message });
                    }
                });
            }
            else
            {
                result.HasError = true;
            }
            return result;
        }

        public ClientResult<ServerResult> Set(string region, string key, object obj, TimeSpan expire, string lockSession, bool addIfNotExist)
        {
            ClientResult<ServerResult> result = new ClientResult<ServerResult>();
            object setobj = CloneCacheObject(obj);
            if (setobj != null)
            {
                this.MapRegion(region).AsParallel().ForAll(server =>
                {
                    try
                    {
                        byte[] temp = (byte[])WcfHelper<ICacheServer>.ExecuteMethod(server, "SetWithLock", region, key, JsonConvert.SerializeObject(setobj), expire, lockSession, addIfNotExist);
                        ServerResult tempresult = ResultProcessor.DeserializeResult<ServerResult>(temp);
                        lock (result)
                        {
                            if (tempresult.Code != CacheCode.OK)
                            {
                                result.HasError = true;
                            }
                        }
                        result.ServerResults.Add(server, tempresult);
                    }
                    catch (Exception ex)
                    {
                        result.ServerResults.Add(server, new ServerResult() { Code = ex.Message });
                    }
                });
            }
            else
            {
                result.HasError = true;
            }
            return result;
        }

        public GetClientResult<T, GetServerResult> Get<T>(string region, string key)
        {
            GetClientResult<T, GetServerResult> result = new GetClientResult<T, GetServerResult>();
            bool allerror = true;
            this.MapRegion(region).AsParallel().ForAll(server =>
            {
                try
                {
                    byte[] temp = (byte[])WcfHelper<ICacheServer>.ExecuteMethod(server, "Get", region, key);
                    GetServerResult serverresult = ResultProcessor.DeserializeResult<GetServerResult>(temp);
                    lock (result)
                    {
                        this.ProcessGet<T>(server, result, serverresult);
                        allerror = false;
                    }
                }
                catch (Exception ex)
                {
                    result.ServerResults.Add(server, new GetServerResult() { Code = ex.Message });
                }
            });
            result.HasError = allerror;
            return result;
        }

        public GetClientResult<T, GetServerResult> Get<T>(string region, string key, string lockSession, int lockSeconds)
        {
            GetClientResult<T, GetServerResult> result = new GetClientResult<T, GetServerResult>();
            DateTime version = DateTime.MinValue;
            bool allerror = true;
            this.MapRegion(region).AsParallel().ForAll(server =>
            {
                try
                {
                    byte[] temp = (byte[])WcfHelper<ICacheServer>.ExecuteMethod(server, "GetWithLock", region, key, lockSession, lockSeconds);
                    GetServerResult serverresult = ResultProcessor.DeserializeResult<GetServerResult>(temp);
                    lock (result)
                    {
                        this.ProcessGet<T>(server, result, serverresult);
                        allerror = false;
                    }
                }
                catch (Exception ex)
                {
                    result.ServerResults.Add(server, new GetServerResult() { Code = ex.Message });
                }
            });
            result.HasError = allerror;
            return result;
        }

        public GetWithFilterClientResult<T, GetWithFilterServerResult> GetWithFilter<T>(string region, string filterExpression)
        {
            GetWithFilterClientResult<T, GetWithFilterServerResult> result = new GetWithFilterClientResult<T, GetWithFilterServerResult>();
            bool allerror = true;
            this.MapRegion(region).AsParallel().ForAll(server =>
            {
                try
                {
                    byte[] temp = (byte[])WcfHelper<ICacheServer>.ExecuteMethod(server, "GetWithFilter", region, filterExpression);
                    GetWithFilterServerResult serverresult = ResultProcessor.DeserializeResult<GetWithFilterServerResult>(temp);
                    lock (result)
                    {
                        this.GetProcess<T>(server, result, serverresult);
                        allerror = false;
                    }
                }
                catch (Exception ex)
                {
                    result.ServerResults.Add(server, new GetWithFilterServerResult() { Code = ex.Message });
                }
            });
            result.HasError = allerror;
            return result;
        }

        public GetWithFilterClientResult<T, GetWithFilterServerResult> GetWithFilter<T>(string region, string filterExpression, string lockSession, int lockSeconds)
        {
            GetWithFilterClientResult<T, GetWithFilterServerResult> result = new GetWithFilterClientResult<T, GetWithFilterServerResult>();
            bool allerror = true;
            this.MapRegion(region).AsParallel().ForAll(server =>
            {
                try
                {
                    byte[] temp = (byte[])WcfHelper<ICacheServer>.ExecuteMethod(server, "GetWithFilterAndLock", region, filterExpression, lockSession, lockSeconds);
                    GetWithFilterServerResult serverresult = ResultProcessor.DeserializeResult<GetWithFilterServerResult>(temp);
                    lock (result)
                    {
                        this.GetProcess<T>(server, result, serverresult);
                        allerror = false;
                    }
                }
                catch (Exception ex)
                {
                    result.ServerResults.Add(server, new GetWithFilterServerResult() { Code = ex.Message });
                }
            });
            result.HasError = allerror;
            return result;
        }

        public GetWithFilterClientResult<T, GetWithFilterServerResult> GetWithFilterUsingView<T>(string region, string filterExpression, string view)
        {
            GetWithFilterClientResult<T, GetWithFilterServerResult> result = new GetWithFilterClientResult<T, GetWithFilterServerResult>();
            bool allerror = true;
            this.MapRegion(region).AsParallel().ForAll(server =>
            {
                try
                {
                    byte[] temp = (byte[])WcfHelper<ICacheServer>.ExecuteMethod(server, "GetWithFilterUsingView", region, filterExpression, view);
                    GetWithFilterServerResult serverresult = ResultProcessor.DeserializeResult<GetWithFilterServerResult>(temp);
                    lock (result)
                    {
                        this.GetProcess<T>(server, result, serverresult);
                        allerror = false;
                    }
                }
                catch (Exception ex)
                {
                    result.ServerResults.Add(server, new GetWithFilterServerResult() { Code = ex.Message });
                }
            });
            result.HasError = allerror;
            return result;
        }

        public GetWithFilterClientResult<T, GetWithFilterServerResult> GetWithFilterUsingView<T>(string region, string filterExpression, string view, string lockSession, int lockSeconds)
        {
            GetWithFilterClientResult<T, GetWithFilterServerResult> result = new GetWithFilterClientResult<T, GetWithFilterServerResult>();
            bool allerror = true;
            this.MapRegion(region).AsParallel().ForAll(server =>
            {
                try
                {
                    byte[] temp = (byte[])WcfHelper<ICacheServer>.ExecuteMethod(server, "GetWithFilterAndLockUsingView", region, filterExpression, view, lockSession, lockSeconds);
                    GetWithFilterServerResult serverresult = ResultProcessor.DeserializeResult<GetWithFilterServerResult>(temp);
                    lock (result)
                    {
                        this.GetProcess<T>(server, result, serverresult);
                        allerror = false;
                    }
                }
                catch (Exception ex)
                {
                    result.ServerResults.Add(server, new GetWithFilterServerResult() { Code = ex.Message });
                }
            });
            result.HasError = allerror;
            return result;
        }

        public GetWithFilterClientResult<T, GetWithFilterServerResult> GetWithFilterAndTop<T>(string region, string filterExpression, int top, string sort)
        {
            GetWithFilterClientResult<T, GetWithFilterServerResult> result = new GetWithFilterClientResult<T, GetWithFilterServerResult>();
            bool allerror = true;
            this.MapRegion(region).AsParallel().ForAll(server =>
            {
                try
                {
                    byte[] temp = (byte[])WcfHelper<ICacheServer>.ExecuteMethod(server, "GetWithFilterAndTop", region, filterExpression, top, sort);
                    GetWithFilterServerResult serverresult = ResultProcessor.DeserializeResult<GetWithFilterServerResult>(temp);
                    lock (result)
                    {
                        this.GetProcess<T>(server, result, serverresult);
                        allerror = false;
                    }
                }
                catch (Exception ex)
                {
                    result.ServerResults.Add(server, new GetWithFilterServerResult() { Code = ex.Message });
                }
            });
            result.HasError = allerror;
            return result;
        }

        public GetWithFilterClientResult<T, GetWithFilterServerResult> GetWithFilterAndTop<T>(string region, string filterExpression, int top, string sort, string lockSession, int lockSeconds)
        {
            GetWithFilterClientResult<T, GetWithFilterServerResult> result = new GetWithFilterClientResult<T, GetWithFilterServerResult>();
            bool allerror = true;
            this.MapRegion(region).AsParallel().ForAll(server =>
            {
                try
                {
                    byte[] temp = (byte[])WcfHelper<ICacheServer>.ExecuteMethod(server, "GetWithFilterAndTopAndLock", region, filterExpression, top, sort, lockSession, lockSeconds);
                    GetWithFilterServerResult serverresult = ResultProcessor.DeserializeResult<GetWithFilterServerResult>(temp);
                    lock (result)
                    {
                        this.GetProcess<T>(server, result, serverresult);
                        allerror = false;
                    }
                }
                catch (Exception ex)
                {
                    result.ServerResults.Add(server, new GetWithFilterServerResult() { Code = ex.Message });
                }
            });
            result.HasError = allerror;
            return result;
        }

        public GetWithFilterClientResult<T, GetWithFilterServerResult> GetWithFilterAndTopUsingView<T>(string region, string filterExpression, string view, int top, string sort)
        {
            GetWithFilterClientResult<T, GetWithFilterServerResult> result = new GetWithFilterClientResult<T, GetWithFilterServerResult>();
            bool allerror = true;
            this.MapRegion(region).AsParallel().ForAll(server =>
            {
                try
                {
                    byte[] temp = (byte[])WcfHelper<ICacheServer>.ExecuteMethod(server, "GetWithFilterAndTopUsingView", region, filterExpression, view, top, sort);
                    GetWithFilterServerResult serverresult = ResultProcessor.DeserializeResult<GetWithFilterServerResult>(temp);
                    lock (result)
                    {
                        this.GetProcess<T>(server, result, serverresult);
                        allerror = false;
                    }
                }
                catch (Exception ex)
                {
                    result.ServerResults.Add(server, new GetWithFilterServerResult() { Code = ex.Message });
                }
            });
            result.HasError = allerror;
            return result;
        }

        public GetWithFilterClientResult<T, GetWithFilterServerResult> GetWithFilterAndTopUsingView<T>(string region, string filterExpression, string view, int top, string sort, string lockSession, int lockSeconds)
        {
            GetWithFilterClientResult<T, GetWithFilterServerResult> result = new GetWithFilterClientResult<T, GetWithFilterServerResult>();
            bool allerror = true;
            this.MapRegion(region).AsParallel().ForAll(server =>
            {
                try
                {
                    byte[] temp = (byte[])WcfHelper<ICacheServer>.ExecuteMethod(server, "GetWithFilterAndTopAndLockUsingView", region, filterExpression, view, top, sort, lockSession, lockSeconds);
                    GetWithFilterServerResult serverresult = ResultProcessor.DeserializeResult<GetWithFilterServerResult>(temp);
                    lock (result)
                    {
                        this.GetProcess<T>(server, result, serverresult);
                        allerror = false;
                    }
                }
                catch (Exception ex)
                {
                    result.ServerResults.Add(server, new GetWithFilterServerResult() { Code = ex.Message });
                }
            });
            result.HasError = allerror;
            return result;
        }

        
        public GetWithExtensionClientResult<T,GetWithExtensionServerResult> GetWithExtension<T>(string region, string extensionName, string extensionValue)
        {
            GetWithExtensionClientResult<T, GetWithExtensionServerResult> result = new GetWithExtensionClientResult<T,GetWithExtensionServerResult>();
            bool allerror = true;
            this.MapRegion(region).AsParallel().ForAll(server =>
            {
                try
                {
                    byte[] temp = (byte[])WcfHelper<ICacheServer>.ExecuteMethod(server, "GetWithExtension", region, extensionName, extensionValue);
                    GetWithExtensionServerResult serverresult = ResultProcessor.DeserializeResult<GetWithExtensionServerResult>(temp);
                    lock (result)
                    {
                        this.GetProcess<T>(server, result, serverresult);
                        allerror = false;
                    }
                }
                catch (Exception ex)
                {
                    result.ServerResults.Add(server, new GetWithExtensionServerResult() { Code = ex.Message });
                }
            });
            result.HasError = allerror;
            return result;
        }

        public GetWithExtensionClientResult<T, GetWithExtensionServerResult> GetWithExtensionAndLock<T>(string region, string extensionName, string extensionValue, string lockSession, int lockSeconds)
        {
            GetWithExtensionClientResult<T, GetWithExtensionServerResult> result = new GetWithExtensionClientResult<T, GetWithExtensionServerResult>();
            bool allerror = true;
            this.MapRegion(region).AsParallel().ForAll(server =>
            {
                try
                {
                    byte[] temp = (byte[])WcfHelper<ICacheServer>.ExecuteMethod(server, "GetWithExtensionAndLock", region, extensionName, extensionValue, lockSession, lockSeconds);
                    GetWithExtensionServerResult serverresult = ResultProcessor.DeserializeResult<GetWithExtensionServerResult>(temp);
                    lock (result)
                    {
                        this.GetProcess<T>(server, result, serverresult);
                        allerror = false;
                    }
                }
                catch (Exception ex)
                {
                    result.ServerResults.Add(server, new GetWithExtensionServerResult() { Code = ex.Message });
                }
            });
            result.HasError = allerror;
            return result;
        }

        public CountClientResult<CountServerResult> Count(string region)
        {
            CountClientResult<CountServerResult> result = new CountClientResult<CountServerResult>();
            this.MapRegion(region).AsParallel().ForAll(server =>
            {
                try
                {
                    byte[] temp = (byte[])WcfHelper<ICacheServer>.ExecuteMethod(server, "Count", region);
                    CountServerResult serverresult = ResultProcessor.DeserializeResult<CountServerResult>(temp);
                    lock (result)
                    {
                        if (serverresult.Code == CacheCode.OK)
                        {
                            result.Counts.Add(server, serverresult.Count.Value);
                        }
                        else
                        {
                            result.HasError = true;
                        }
                        result.ServerResults.Add(server, serverresult);
                    }
                }
                catch (Exception ex)
                {
                    result.ServerResults.Add(server, new CountServerResult() { Code = ex.Message });
                }
            });
            return result;
        }

        //performace is low , pls use Count2WithFilter
        public Count2ClientResult<Count2ServerResult> Count2(string region)
        {
            Count2ClientResult<Count2ServerResult> result = new Count2ClientResult<Count2ServerResult>();
            List<string> tempforcount = new List<string>();
            this.MapRegion(region).AsParallel().ForAll(server =>
            {
                try
                {
                    byte[] temp = (byte[])WcfHelper<ICacheServer>.ExecuteMethod(server, "Count2", region);
                    Count2ServerResult serverresult = ResultProcessor.DeserializeResult<Count2ServerResult>(temp);
                    lock (result)
                    {
                        if (serverresult.Code == CacheCode.OK)
                        {
                            tempforcount.AddRange(serverresult.AllCacheJsons);
                        }
                        else
                        {
                            result.HasError = true;
                        }
                        result.ServerResults.Add(server, serverresult);
                    }
                }
                catch (Exception ex)
                {
                    result.ServerResults.Add(server, new Count2ServerResult() { Code = ex.Message });
                }
            });
            result.Count = tempforcount.Distinct().LongCount();
            return result;
        }

        public Count2ClientResult<Count2ServerResult> Count2WithFilter(string region, string filterExpression)
        {
            Count2ClientResult<Count2ServerResult> result = new Count2ClientResult<Count2ServerResult>();
            List<string> tempforcount = new List<string>();
            this.MapRegion(region).AsParallel().ForAll(server =>
            {
                try
                {
                    byte[] temp = (byte[])WcfHelper<ICacheServer>.ExecuteMethod(server, "Count2WithFilter", region,filterExpression);
                    Count2ServerResult serverresult = ResultProcessor.DeserializeResult<Count2ServerResult>(temp);
                    lock (result)
                    {
                        if (serverresult.Code == CacheCode.OK)
                        {
                            tempforcount.AddRange(serverresult.AllCacheJsons);
                        }
                        else
                        {
                            result.HasError = true;
                        }
                        result.ServerResults.Add(server, serverresult);
                    }
                }
                catch (Exception ex)
                {
                    result.ServerResults.Add(server, new Count2ServerResult() { Code = ex.Message });
                }
            });
            result.Count = tempforcount.Distinct().LongCount();
            return result;
        }

        public Count2ClientResult<Count2ServerResult> Count2WithExtension(string region, string extensionName, string extensionValue)
        {
            Count2ClientResult<Count2ServerResult> result = new Count2ClientResult<Count2ServerResult>();
            List<string> tempforcount = new List<string>();
            this.MapRegion(region).AsParallel().ForAll(server =>
            {
                try
                {
                    byte[] temp = (byte[])WcfHelper<ICacheServer>.ExecuteMethod(server, "Count2WithExtension", region, extensionName, extensionValue);
                    Count2ServerResult serverresult = ResultProcessor.DeserializeResult<Count2ServerResult>(temp);
                    lock (result)
                    {
                        if (serverresult.Code == CacheCode.OK)
                        {
                            tempforcount.AddRange(serverresult.AllCacheJsons);
                        }
                        else
                        {
                            result.HasError = true;
                        }
                        result.ServerResults.Add(server, serverresult);
                    }
                }
                catch (Exception ex)
                {
                    result.ServerResults.Add(server, new Count2ServerResult() { Code = ex.Message });
                }
            });
            result.Count = tempforcount.Distinct().LongCount();
            return result;
        }

        public ClientResult<ServerResult> Remove(string region, string key)
        {
            ClientResult<ServerResult> result = new ClientResult<ServerResult>();
            this.MapRegion(region).AsParallel().ForAll(server =>
            {
                try
                {
                    byte[] temp = (byte[])WcfHelper<ICacheServer>.ExecuteMethod(server, "Remove", region, key);
                    ServerResult serverresult = ResultProcessor.DeserializeResult<ServerResult>(temp);
                    lock (result)
                    {
                        if (serverresult.Code != CacheCode.OK)
                        {
                            result.HasError = true;
                        }
                        result.ServerResults.Add(server, serverresult);
                    }
                }
                catch (Exception ex)
                {
                    result.ServerResults.Add(server, new ServerResult() { Code = ex.Message });
                }
            });
            return result;
        }

        public ClientResult<ServerResult> Remove(string region, string key, string lockSession)
        {
            ClientResult<ServerResult> result = new ClientResult<ServerResult>();
            this.MapRegion(region).AsParallel().ForAll(server =>
            {
                try
                {
                    byte[] temp = (byte[])WcfHelper<ICacheServer>.ExecuteMethod(server, "RemoveWithLock", region, key, lockSession);
                    ServerResult serverresult = ResultProcessor.DeserializeResult<ServerResult>(temp);
                    lock (result)
                    {
                        if (serverresult.Code != CacheCode.OK)
                        {
                            result.HasError = true;
                        }
                        result.ServerResults.Add(server, serverresult);
                    }
                }
                catch (Exception ex)
                {
                    result.ServerResults.Add(server, new ServerResult() { Code = ex.Message });
                }
            });
            return result;
        }

        public void Dispose()
        {
            _regionMaps.Clear();
        }
    }
}
